package cate.game.activity.base;

import cate.common.table.activity.MergeDayActivityBaseRow;
import cate.common.table.activity.NaturalActivityBaseRow;
import cate.common.table.activity.OpenDayActivityBaseRow;
import cate.common.table.activity.OpenDayRoundActBaseRow;
import cate.common.table.d.GDActivity;
import cate.common.util.TimeTool;
import cate.game.activity.base.core.ActivityGlobalData;
import cate.game.activity.base.core.ActivityHandler;
import cate.game.activity.base.spo.ActivityGlobalSPO;
import cate.game.activity.config.ActivityBaseCfgPO;
import cate.game.db.facade.KvLoader;
import cate.game.db.mongo.kv.KvMongo;
import cate.game.framework.ModuleMan;
import cp.solution.ops.status.ModOprStatus;
import easy.java.dev.note.NoteField;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class ActivityGlobalManager extends ModuleMan {
    @NoteField("全服数据")
    public ActivityGlobalSPO spo;
    @NoteField("活动处理")
    public Map<Integer, ActivityHandler<?, ?>> handlers;

    @Override
    public void initDependent() throws Exception {
        // 如果需要更细致地处理合服逻辑，就将 getBySrvId()的策略改成 getWithMerge()
        spo = new KvLoader<>(game, ActivityGlobalSPO.class, KvMongo.KEY_ACTIVITY_GLOBAL).getBySrvId();
        handlers = new HashMap<>();
        Set<Integer> funcIds = ActivityLoader.getInstance().allActivityFuncIds();
        for (int funcId : funcIds) {
            ActivityHandler<?, ?> handler = ActivityLoader.getInstance().build(funcId);
            if (handler == null) {
                log.error("构建活动处理类失败! funcId=" + funcId);
                continue;
            }
            handler.initIndependent(game);
            ActivityGlobalData globalData = spo.globalDataMap.get(funcId);
            if (globalData == null) {
                globalData = handler.buildGlobal();
                globalData.init(funcId);
                spo.globalDataMap.put(funcId, globalData);
            }
            ActivityGlobalData newData = handler.setup(funcId, globalData);
            if (newData != null) {
                spo.globalDataMap.put(funcId, newData);
            }
            handler.initialize(game);
            handlers.put(funcId, handler);
        }
        for (int funcId : funcIds) {
            ActivityBaseCfgPO cfg = loadOpeningActivityConfig(funcId);
            Optional.ofNullable(handlers.get(funcId)).ifPresent(handler -> {
                handler.setActivityConf(cfg);
                handler.resetCheckTime();
            });
        }
    }

    /**
     * 加载活动时间配置
     *
     * @param funcId 功能ID
     */
    public ActivityBaseCfgPO loadOpeningActivityConfig(int funcId) {
        ActivityBaseCfgPO po = loadOpenDay(funcId, true);
        if (po != null) {
            return po;
        }
        po = loadRoundDay(funcId, true);
        if (po != null) {
            return po;
        }
        po = loadNaturalDay(funcId, true);
        if (po != null) {
            return po;
        }
        po = loadMergeDay(funcId, true);
        return Optional.ofNullable(po).orElse(ActivityBaseCfgPO.EMPTY(funcId));
    }

    /**
     * 加载最近的即将开启的活动时间配置
     *
     * @param funcId 功能ID
     */
    public ActivityBaseCfgPO loadComingActivityConfig(int funcId) {
        ActivityBaseCfgPO po = loadOpenDay(funcId, false);
        po = earlier(po, loadRoundDay(funcId, false));
        po = earlier(po, loadNaturalDay(funcId, false));
        po = earlier(po, loadMergeDay(funcId, false));
        return Optional.ofNullable(po).orElse(ActivityBaseCfgPO.EMPTY(funcId));
    }

    /**
     * 选取更早的触发的配置
     */
    private static ActivityBaseCfgPO earlier(ActivityBaseCfgPO po1, ActivityBaseCfgPO po2) {
        if (po1 == null) {
            return po2;
        }
        if (po2 == null) {
            return po1;
        }
        if (po1.startTime < po2.startTime) {
            return po1;
        }
        return po2;
    }

    /**
     * 开服天数活动
     *
     * @param funcId 功能ID
     * @param current <p>true 当前进行中</p>
     *                <p>false 即将进行</p>
     * @return 基础配置 未找到为null
     */
    private ActivityBaseCfgPO loadOpenDay(int funcId, boolean current) {
        long nowTime = game.time.now();
        ActivityBaseCfgPO cfgPO = null;
        for (OpenDayActivityBaseRow row : game.table.activity.openDay.getList()) {
            if (row.funcId != funcId) {
                continue;
            }
            long startTime = TimeTool.calTimeStamp(game.getOpenTime(), row.startDay, row.startTimeStr);
            long endTime = TimeTool.calTimeStamp(game.getOpenTime(), row.endDay, row.endTimeStr);
            if (endTime <= nowTime) {
                continue;
            }
            if (current && startTime > nowTime) {
                continue;
            }
            ActivityBaseCfgPO cc = new ActivityBaseCfgPO();
            cc.activityId = row.activityId;
            cc.funcId = funcId;
            cc.configTid = row.configId;
            cc.startTime = startTime;
            cc.endTime = endTime;
            if (current && cfgPO != null) {
                log.error("功能id={}的活动时间配置重复,活动ID={}", cc.funcId, cc.activityId);
            } else {
                cfgPO = earlier(cfgPO, cc);
            }
        }
        return cfgPO;
    }

    /**
     * 开服循环天数活动
     *
     * @param funcId 功能ID
     * @param current <p>true 当前进行中</p>
     *                <p>false 即将进行</p>
     * @return 基础配置 未找到为null
     */
    private ActivityBaseCfgPO loadRoundDay(int funcId, boolean current) {
        long nowTime = game.time.now();
        ActivityBaseCfgPO cfgPO = null;
        for (OpenDayRoundActBaseRow row : game.table.activity.openDayRound.getList()) {
            if (row.funcId != funcId) {
                continue;
            }
            long startTime = TimeTool.calTimeStamp(game.getOpenTime(), row.startDay, row.startTimeStr);
            long endTime = TimeTool.calTimeStamp(game.getOpenTime(), row.startDay + row.durationDay, null) - 1000L;
            int roundTime = row.roundTime == 0 ? 99999 : row.roundTime;
            for (int i = 0; i < roundTime; i++) {
                if (endTime > nowTime) {
                    break;
                }
                startTime += row.roundDay * TimeTool.TimeCons.DAY;
                endTime += row.roundDay * TimeTool.TimeCons.DAY;
            }
            if (endTime <= nowTime) {
                continue;
            }
            if (current && startTime > nowTime) {
                continue;
            }
            ActivityBaseCfgPO cc = new ActivityBaseCfgPO();
            cc.activityId = row.activityId;
            cc.funcId = funcId;
            cc.configTid = row.configId;
            cc.startTime = startTime;
            cc.endTime = endTime;
            if (current && cfgPO != null) {
                log.error("功能id={}的活动时间配置重复,活动ID={}", cc.funcId, cc.activityId);
            } else {
                cfgPO = earlier(cfgPO, cc);
            }
        }
        return cfgPO;
    }

    /**
     * 自然日活动
     *
     * @param funcId 功能ID
     * @param current <p>true 当前进行中</p>
     *                <p>false 即将进行</p>
     * @return 基础配置 未找到为null
     */
    private ActivityBaseCfgPO loadNaturalDay(int funcId, boolean current) {
        long nowTime = game.time.now();
        ActivityBaseCfgPO cfgPO = null;
        for (NaturalActivityBaseRow row : game.table.activity.natural.getList()) {
            if (row.funcId != funcId) {
                continue;
            }
            long startTime = TimeTool.parseTime(row.startTimeStr);
            long endTime = TimeTool.parseTime(row.endTimeStr);
            if (endTime <= nowTime) {
                continue;
            }
            //有开服天数限制的活动
            if (row.openDay > 0) {
                if (row.openDayType == GDActivity.OpenDayType.FIRST_DAY) {
                    //活动开始时是开服第几天
                    int openDay = TimeTool.getDistanceDays(game.getOpenTime(), startTime) + 1;
                    if (openDay < row.openDay) {
                        continue;
                    }
                }
                if (row.openDayType == GDActivity.OpenDayType.MIDWAY) {
                    //根据开服天数限制推测的活动开启时间
                    long openDayStartTime = TimeTool.calTimeStamp(game.getOpenTime(), row.openDay);
                    if (openDayStartTime >= endTime) {
                        continue;
                    }
                    if (openDayStartTime > startTime) {
                        startTime = openDayStartTime;
                    }
                }
            }
            if (current && startTime > nowTime) {
                continue;
            }
            ActivityBaseCfgPO cc = new ActivityBaseCfgPO();
            cc.activityId = row.activityId;
            cc.funcId = funcId;
            cc.configTid = row.configId;
            cc.startTime = startTime;
            cc.endTime = endTime;
            if (current && cfgPO != null) {
                log.error("功能id={}的活动时间配置重复,活动ID={}", cc.funcId, cc.activityId);
            } else {
                cfgPO = earlier(cfgPO, cc);
            }
        }
        return cfgPO;
    }

    /**
     * 合服天数活动
     *
     * @param funcId 功能ID
     * @param current <p>true 当前进行中</p>
     *                <p>false 即将进行</p>
     * @return 基础配置 未找到为null
     */
    private ActivityBaseCfgPO loadMergeDay(int funcId, boolean current) {
        if (game.getMergeTime() <= 0) {
            return null;
        }
        long nowTime = game.time.now();
        ActivityBaseCfgPO cfgPO = null;
        for (MergeDayActivityBaseRow row : game.table.activity.merge.getList()) {
            if (row.funcId != funcId) {
                continue;
            }
            long startTime = TimeTool.calTimeStamp(game.getMergeTime(), row.startDay, row.startTimeStr);
            long endTime = TimeTool.calTimeStamp(game.getMergeTime(), row.endDay, row.endTimeStr);
            if (endTime <= nowTime) {
                continue;
            }
            if (current && startTime > nowTime) {
                continue;
            }
            ActivityBaseCfgPO cc = new ActivityBaseCfgPO();
            cc.activityId = row.activityId;
            cc.funcId = funcId;
            cc.configTid = row.configId;
            cc.startTime = startTime;
            cc.endTime = endTime;
            if (current && cfgPO != null) {
                log.error("功能id={}的活动时间配置重复,活动ID={}", cc.funcId, cc.activityId);
            } else {
                cfgPO = earlier(cfgPO, cc);
            }
        }
        return cfgPO;
    }

    /**
     * 循环
     */
    public void tick() {
        handlers.values().forEach(ActivityHandler::tick);
    }

    @Override
    public void shutdown() {
        super.shutdown();
        game.db.kv.save(spo);
    }

    /**
     * 尝试获取指定类型的活动处理
     * 未开启的活动也可获得
     *
     * @param funcId 功能ID
     */
    @SuppressWarnings("unchecked")
    public <HANDLER extends ActivityHandler<?, ?>> HANDLER getHandler(int funcId) {
        try {
            ActivityHandler<?, ?> handler = handlers.get(funcId);
            if (handler != null) {
                return (HANDLER) handler;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 尝试获取开启的指定类型的活动处理
     *
     * @param funcId 功能ID
     */
    @SuppressWarnings("unchecked")
    public <HANDLER extends ActivityHandler<?, ?>> HANDLER getEnableHandler(int funcId) {
        try {
            ActivityHandler<?, ?> handler = handlers.get(funcId);
            if (handler != null && handler.getGlobalData().base.active()) {
                return (HANDLER) handler;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取所有的蛋类活动处理
     *
     * @param clazz 活动处理类
     */
    @SuppressWarnings("unchecked")
    public <HANDLER extends ActivityHandler<?, ?>> List<HANDLER> getHandlerList(Class<HANDLER> clazz) {
        return handlers.values().stream().filter(handler -> handler.getClass().equals(clazz))
                .map(handler -> (HANDLER) handler)
                .collect(Collectors.toList());
    }

    /**
     * 获取开启的单类活动处理
     *
     * @param clazz 活动处理类
     */
    @SuppressWarnings("unchecked")
    public <HANDLER extends ActivityHandler<?, ?>> List<HANDLER> getEnableHandlerList(Class<HANDLER> clazz) {
        return handlers.values().stream().filter(handler -> handler.getClass().equals(clazz))
                .filter(handler -> handler.getGlobalData().base.active())
                .map(handler -> (HANDLER) handler)
                .collect(Collectors.toList());
    }

    @Override
    public ModOprStatus handleMergeSettle() {
        ModOprStatus status = new ModOprStatus("活动Global");
        return status.exe(() -> {
            handlers.values().forEach(h -> {
                if(h != null) {
                    try {
                        h.handleMergeSettle();
                    } catch (Exception e) {
                        log.error("", e);
                        status.addLog(h.getClass().getName() + " : "+ e.getMessage());
                    }
                }
            });
        });
    }
}
